package org.dandsoft.blackbox.labs.snn;

import java.io.Reader;
import java.io.IOException;

/**
 * User: dvilyuzhanin
 * Date: 26.01.2009 17:17:55
 */
public class SynapsesReader {

    public final static String STOP_PATTERN = ",:;{}";

    static enum State {

        READ_LAYER {
            public State nextState(char lastChar) {
                if (lastChar == SynapsesWriter.FIELDS_SEPARATOR) {
                    return READ_SOURCE;
                } else if (lastChar == SynapsesWriter.LAYERS_SEPARATOR) {
                    return READ_LAYER;
                } else {
                    return null;
                }
            }},

        READ_SOURCE {
            public State nextState(char lastChar) {
                if (lastChar == SynapsesWriter.SYNAPSES_SOURCE_SEPARATOR) {
                    return READ_DESTINATION;
                } else if (lastChar == SynapsesWriter.LAYER_INFO_START) {
                    return READ_LAYER_INFO;
                } else {
                    return null;
                }
            }},

        READ_DESTINATION {
            public State nextState(char lastChar) {
                if (lastChar == SynapsesWriter.FIELDS_SEPARATOR) {
                    return READ_SOURCE;
                } else if (lastChar == SynapsesWriter.SYNAPSES_DESTINATIONS_SEPARATOR) {
                    return READ_DESTINATION;
                }
                return null;
            }},

        READ_LAYER_INFO {
            public State nextState(char lastChar) {
                return null;
            }},

        WAIT_NEW_LAYER {
            public State nextState(char lastChar) {
                return null;
            }};

        public abstract State nextState(char lastChar);
    }

    private int layerNumber = -1;
    private Reader reader;
    private State state = State.READ_LAYER;
    private char lastChar;
    private int sourceNeuron;

    public SynapsesReader(Reader reader) {
        this.reader = reader;
    }

    public synchronized int readSynapses(int[][] synapses) throws IOException {
        return readSynapses(synapses, 0, synapses.length);
    }

    public synchronized int readSynapses(int[][] synapses, int begin, int length) throws IOException {
        int synapsesCount = 0;

        while (synapsesCount <  length) {
            if (State.READ_SOURCE == state) {
                String value = readUntil(STOP_PATTERN);
                state = state.nextState(lastChar);
                if (State.READ_LAYER_INFO == state) {
                    readUntil("\n");
                    state = State.READ_LAYER;
                    break;
                } else if (State.READ_DESTINATION == state) {
                    sourceNeuron = Integer.decode("#" + value);
                }

            } else if (State.READ_DESTINATION == state) {
                String value = readUntil(STOP_PATTERN);
                state = state.nextState(lastChar);
                int destinationNeuron = Integer.decode("#" + value);

                synapses[begin + synapsesCount][0] = sourceNeuron;
                synapses[begin + synapsesCount][1] = destinationNeuron;
                synapsesCount++;

            }
        }
        return synapsesCount;
    }

    public synchronized boolean readNextLayer() throws IOException {
        if (State.READ_LAYER == state) {
            if (reader.read() == -1) {
                return false;
            } else {
                String value = readUntil(STOP_PATTERN);
                state = state.nextState(lastChar);
                layerNumber = Integer.decode("#" + value);
                return true;
            }

        }
        return true;
    }


    private String readUntil(String stopPattern) throws IOException {
        StringBuilder builder = new StringBuilder(32);
        int ch;
        while (((ch = reader.read()) != -1) && (stopPattern.indexOf((char) ch) == -1)) {
            builder.append((char)ch);
        }
        lastChar = (char) ch;

        return builder.toString();
    }

    public synchronized int getLayerNumber() {
        return layerNumber;
    }
}
